#include "in.h"

int TestRecordMatched( char *record_data , struct MemField *where_field_array , size_t where_field_array_count , void *where_record )
{
	size_t		where_field_array_no ;
	int		is_matched ; /* 0:matched !0:not matched */
	
	for( where_field_array_no = 0 ; where_field_array_no < where_field_array_count ; where_field_array_no++ )
	{
		TRACE_PRINTF( "MEMCMP where_field_array_no[%zu] .field_offset[%d] .field_size[%zu] record_data[%p]\n" , where_field_array_no , where_field_array[where_field_array_no].field_offset , where_field_array[where_field_array_no].field_size , record_data );
		TRACE_PRINTF( "     where_record.field_data" ); TRACE_PRINTHEX( where_record+where_field_array[where_field_array_no].field_offset , where_field_array[where_field_array_no].field_size ); TRACE_PRINTEOL();
		TRACE_PRINTF( "           record.field_data" ); TRACE_PRINTHEX( record_data+where_field_array[where_field_array_no].field_offset , where_field_array[where_field_array_no].field_size ); TRACE_PRINTEOL();
		is_matched = memcmp( where_record+where_field_array[where_field_array_no].field_offset , record_data+where_field_array[where_field_array_no].field_offset , where_field_array[where_field_array_no].field_size ) ;
		TRACE_PRINTF( "MEMCMP is_matched[%d]\n" , is_matched );
		if( is_matched )
			return is_matched;
	}
	
	return 0;
}

char *QueryFirstRecordExisted( struct MemTable *table )
{
	struct MemDataPage		*record_datapage = NULL ;
	char				*record_dataunit = NULL ;
	char				*record_data = NULL ;
	uint64_t			used_bitmap ;
	
	list_for_each_entry( record_datapage , & (table->record_datapage_list) , struct MemDataPage , datapage_list_node )
	{
		TRACE_PRINTF( "record_datapage[%p] .dataunit_size[%zu] .used_count[%zu/%zu] \n" , record_datapage , record_datapage->dataunit_size , record_datapage->dataunit_used_count,record_datapage->dataunit_prealloc_count );
		used_bitmap = 1 ;
		used_bitmap = ( used_bitmap << (sizeof0(struct MemDataPage,dataunit_used_bitmap)*8-1) ) ;
		record_dataunit = record_datapage->dataunit_array_base ;
		record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
		while( used_bitmap )
		{
			TRACE_PRINTF( "record_dataunit[%p][%p] used_bitmap[0x%lX]&[0x%lX]=[0x%lX]\n" , record_dataunit , record_data , used_bitmap , record_datapage->dataunit_used_bitmap , used_bitmap & record_datapage->dataunit_used_bitmap ); if( record_data == NULL ) record_data = NULL ;
			if( used_bitmap & record_datapage->dataunit_used_bitmap )
				return record_dataunit;
			
			used_bitmap >>= 1 ;
			record_dataunit += record_datapage->dataunit_size ;
			record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
		}
	}
	
	return NULL;
}

char *QueryNextRecordExisted( struct MemTable *table , char *begin_record_dataunit )
{
	char				*record_dataunit = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	char				*record_data = NULL ;
	struct MemDataPage		*record_datapage = NULL ;
	uint64_t			used_bitmap ;
	
	record_dataunit = begin_record_dataunit ;
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_datapage = record_dataunit_head->record_datapage ;
	used_bitmap = 1 ;
	used_bitmap = used_bitmap << ( sizeof0(struct MemDataPage,dataunit_used_bitmap)*8-1 - (record_dataunit-record_datapage->dataunit_array_base)/record_datapage->dataunit_size ) ;
	used_bitmap >>= 1 ;
	record_dataunit += record_datapage->dataunit_size ;
	record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
	for( ; ; )
	{
		TRACE_PRINTF( "record_datapage[%p] .dataunit_size[%zu] .used_count[%zu/%zu]\n" , record_datapage , record_datapage->dataunit_size , record_datapage->dataunit_used_count,record_datapage->dataunit_prealloc_count );
		while( used_bitmap )
		{
			TRACE_PRINTF( "record_dataunit[%p][%p] used_bitmap[0x%lX]&[0x%lX]=[0x%lX]\n" , record_dataunit , record_data , used_bitmap , record_datapage->dataunit_used_bitmap , used_bitmap & record_datapage->dataunit_used_bitmap ); if( record_data == NULL ) record_data = NULL ;
			if( used_bitmap & record_datapage->dataunit_used_bitmap )
				return record_dataunit;
			
			used_bitmap >>= 1 ;
			record_dataunit += record_datapage->dataunit_size ;
			record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
		}
		
		record_datapage = list_next_entry_or_null( record_datapage , & (table->record_datapage_list) , struct MemDataPage , datapage_list_node ) ;
		if( record_datapage == NULL )
			break;
		
		used_bitmap = 1 ;
		used_bitmap = used_bitmap << ( sizeof0(struct MemDataPage,dataunit_used_bitmap)*8-1 ) ;
		record_dataunit = record_datapage->dataunit_array_base ;
		record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
	}
	
	return NULL;
}

char *QueryFirstRecordWithoutIndex( struct MemTable *table , void *where_record , struct MemField *where_field_array , size_t where_field_array_count )
{
	struct MemDatabase		*db = table->db ;
	char				*record_dataunit = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	char				*record_data = NULL ;
	int				is_matched ;
	
	record_dataunit = QueryFirstRecordExisted( table ) ;
	if( record_dataunit == NULL )
		return NULL;
	
	for( ; ; )
	{
		record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
		if(	db->txn.txn_no >= record_dataunit_head->insert_txn_id
			&& db->txn.txn_no < record_dataunit_head->delete_txn_id )
		{
			if( where_record == NULL )
				return record_dataunit;
			record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
			is_matched = TestRecordMatched( record_data , where_field_array , where_field_array_count , where_record ) ;
			if( is_matched == 0 )
				return record_dataunit;
		}
		
		record_dataunit = QueryNextRecordExisted( table , record_dataunit ) ;
		if( record_dataunit == NULL )
			return NULL;
	}
}

char *QueryNextRecordWithoutIndex( struct MemTable *table , char *begin_record_dataunit , void *where_record , struct MemField *where_field_array , size_t where_field_array_count )
{
	struct MemDatabase		*db = table->db ;
	char				*record_dataunit = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	char				*record_data = NULL ;
	int				is_matched ;
	
	record_dataunit = QueryNextRecordExisted( table , begin_record_dataunit ) ;
	if( record_dataunit == NULL )
		return record_dataunit;
	
	for( ; ; )
	{
		record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
		if(	db->txn.txn_no >= record_dataunit_head->insert_txn_id
			&& db->txn.txn_no < record_dataunit_head->delete_txn_id )
		{
			if( where_record == NULL )
				return record_dataunit;
			record_data = MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) ;
			is_matched = TestRecordMatched( record_data , where_field_array , where_field_array_count , where_record ) ;
			if( is_matched == 0 )
				return record_dataunit;
		}
		
		record_dataunit = QueryNextRecordExisted( table , record_dataunit ) ;
		if( record_dataunit == NULL )
			return NULL;
	}
}

void CopyRecordFields( char *record_data , struct MemField *select_field_array , size_t select_field_array_count , char *select_record )
{
	size_t		select_field_array_no ;
	
	for( select_field_array_no = 0 ; select_field_array_no < select_field_array_count ; select_field_array_no++ )
	{
		memcpy( select_record+select_field_array[select_field_array_no].field_offset , record_data+select_field_array[select_field_array_no].field_offset , select_field_array[select_field_array_no].field_size );
		TRACE_PRINTF( "memcpy record_data[%p] to select_record[%p] .field_offset[%d] .field_size[%zu] " , record_data , select_record , select_field_array[select_field_array_no].field_offset , select_field_array[select_field_array_no].field_size ); TRACE_PRINTHEX( select_record , select_field_array[select_field_array_no].field_size ); TRACE_PRINTEOL();
	}
	
	return;
}

char *GetUnusedRecordDataUnit( struct MemTable *table )
{
	struct MemDataPage		*record_datapage = NULL ;
	char				*record_dataunit = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	
	record_datapage = list_last_entry_or_null( & (table->record_datapage_list) , struct MemDataPage , datapage_list_node ) ;
	TRACE_PRINTF( "record_datapage[%p] .dataunit_used_count[%zu] .dataunit_prealloc_count[%zu]\n" , record_datapage , record_datapage?record_datapage->dataunit_used_count:0 , record_datapage?record_datapage->dataunit_prealloc_count:0 );
	if( record_datapage == NULL || IsDataPageFull(record_datapage) )
	{
		record_datapage = CreateDataPage( table , sizeof(struct MemRecordDataUnitHead)+table->record_size , & (table->record_datapage_list) , & (table->record_datapage_count) ) ;
		if( record_datapage == NULL )
		{
			tls_last_errno = MDB_ERROR_ALLOC ;
			return NULL;
		}
		
		TRACE_PRINTF( "add record_datapage[%p] to list , .dataunit_size[%zu]\n" , record_datapage , record_datapage->dataunit_size );
	}
	
	record_dataunit = GetUnusedDataUnit( record_datapage ) ;
	if( record_dataunit == NULL )
	{
		TRACE_PRINTF( "GetUnusedDataUnit record_dataunit[%p]\n" , record_dataunit );
		return NULL;
	}
	else
	{
		TRACE_PRINTF( "GetUnusedDataUnit record_dataunit[%p][%p]\n" , record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) );
	}
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_dataunit_head->record_datapage = record_datapage ;
	record_dataunit_head->insert_txn_id = UINT64_MAX ;
	record_dataunit_head->delete_txn_id = 0 ;
	record_dataunit_head->record_no = table->max_record_no ;
	table->max_record_no++;
	
	return record_dataunit;
}

void SetRecordDataUnitUnused( char *record_dataunit )
{
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	struct MemDataPage		*record_datapage = NULL ;
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_datapage = record_dataunit_head->record_datapage ;
	
	TRACE_PRINTF( "SetDataUnitUnused record_data[%p]\n" , MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) );
	SetDataUnitUnused( record_datapage , record_dataunit );
	
	return;
}

char *GetUnusedUncommitRecordDataUnit( struct MemTable *table )
{
	struct MemDataPage		*record_datapage = NULL ;
	char				*record_dataunit = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	
	record_datapage = list_last_entry_or_null( & (table->uncommit_record_datapage_list) , struct MemDataPage , datapage_list_node ) ;
	TRACE_PRINTF( "record_datapage[%p] .dataunit_used_count[%zu] .dataunit_prealloc_count[%zu]\n" , record_datapage , record_datapage?record_datapage->dataunit_used_count:0 , record_datapage?record_datapage->dataunit_prealloc_count:0 );
	if( record_datapage == NULL || IsDataPageFull(record_datapage) )
	{
		record_datapage = CreateDataPage( table , sizeof(struct MemRecordDataUnitHead)+table->record_size , & (table->uncommit_record_datapage_list) , & (table->uncommit_record_datapage_count) ) ;
		if( record_datapage == NULL )
		{
			tls_last_errno = MDB_ERROR_ALLOC ;
			return NULL;
		}
		
		TRACE_PRINTF( "add record_datapage[%p] to list , .dataunit_size[%zu]\n" , record_datapage , record_datapage->dataunit_size );
	}
	
	record_dataunit = GetUnusedDataUnit( record_datapage ) ;
	if( record_dataunit == NULL )
	{
		TRACE_PRINTF( "GetUnusedDataUnit record_dataunit[%p]\n" , record_dataunit );
		return NULL;
	}
	else
	{
		TRACE_PRINTF( "GetUnusedDataUnit record_dataunit[%p][%p]\n" , record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) );
	}
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_dataunit_head->record_datapage = record_datapage ;
	
	return record_dataunit;
}

void SetUncommitRecordDataUnitUnused( char *record_dataunit )
{
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	struct MemDataPage		*record_datapage = NULL ;
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_datapage = record_dataunit_head->record_datapage ;
	
	TRACE_PRINTF( "SetUncommitDataUnitUnused record_data[%p]\n" , MDB_RECORD_DATAUNIT_TO_DATA(record_dataunit) );
	SetDataUnitUnused( record_datapage , record_dataunit );
	
	return;
}

